package com.ipan.kits.xconverter;

import java.text.ParseException;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;

import com.ipan.kits.time.DateFormatUtil;

/**
 * 针对JDBC的ResultSet做的默认的类型转换实现
 * 
 * 除了Blob、Clob、NClob没有实现，针对ResultSet返回的类型都实现了；
 * 其实Blob可以对应为byte[]，Clob、NClob可以对应为String；
 * 或者自己实现一下IXConverter；
 * 
 * 参考mysql的JDBC与Java类型映射
 * https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-type-conversions.html
 * 
 * @author iPan
 * @version 2019-06-06
 */
public class XConverters {
	private static final String[] defaultParsePattern = new String[] {
			"yyyy-MM-dd HH:mm:ss", 
			"yyyy/MM/dd HH:mm:ss", 
			"yyyyMMdd HH:mm:ss",
			"yyyy-MM-dd",
			"yyyy/MM/dd",
			"yyyyMMddHHmmss", // 这个必须放前面
			"yyyyMMdd"
			};
	private static final String timeStampPattern = "yyyy-MM-dd HH:mm:ss";
	private static final String datePattern = "yyyy-MM-dd";
	private static final int dateLen = datePattern.length();
	private static final int timeStampWithoutSecPatternLen = "yyyy-MM-dd HH:mm".length();
	private static final int timePatternLen = "HH:mm:ss".length();
	private static final int timeWithoutSecPatternLen = "HH:mm".length();
	
	private XConverters() {}
	
	private static void checkType(Class<?> type, Object val) {
		Validate.notNull(type);
		Validate.notNull(val);
		if (!val.getClass().isAssignableFrom(type)) { // type是否为Val的子类（type是否属于等于Val）
			throw new java.lang.IllegalArgumentException(val.getClass().getName() + "不属于类型" + type.getName());
		}
	}
	
	public static class IntegerConverter implements IXConverter<Integer> {
		// mysql type: int, integer, tinyint(n) n > 1, smallint, mediumint
		@Override
		public Integer convert(String s) {
			if (s.indexOf(".") > -1) { // 小数截断，否则会异常；
				s = s.substring(0, s.indexOf("."));
			}
			return Integer.parseInt(s);
		}
		
		@Override
		public String unConvert(Object v) {
			checkType(Integer.class, v);
			return (v == null) ? null : v.toString();
		}
	}
	
	// 支持需要保持 short 而非转成 int 的场景
	public static class ShortConverter implements IXConverter<Short> {
		@Override
		public Short convert(String s) {
			if (s.indexOf(".") > -1) { // 小数截断，否则会异常；
				s = s.substring(0, s.indexOf("."));
			}
			return Short.parseShort(s);
		}
		
		@Override
		public String unConvert(Object v) {
			checkType(Short.class, v);
			return (v == null) ? null : v.toString();
		}
	}
	
	// 支持需要保持 byte 而非转成 int 的场景
	public static class ByteConverter implements IXConverter<Byte> {
		@Override
		public Byte convert(String s) {
			return Byte.parseByte(s);
		}
		
		@Override
		public String unConvert(Object v) {
			checkType(Byte.class, v);
			return (v == null) ? null : v.toString();
		}
	}
	
	public static class LongConverter implements IXConverter<Long> {
		// mysql type: bigint
		@Override
		public Long convert(String s) {
			return Long.parseLong(s);
		}
		
		@Override
		public String unConvert(Object v) {
			checkType(Long.class, v);
			return (v == null) ? null : v.toString();
		}
	}
	
	public static class FloatConverter implements IXConverter<Float> {
		// mysql type: float
		@Override
		public Float convert(String s) {
			return Float.parseFloat(s);
		}
		
		@Override
		public String unConvert(Object v) {
			checkType(Float.class, v);
			return (v == null) ? null : v.toString();
		}
	}
	
	public static class DoubleConverter implements IXConverter<Double> {
		// mysql type: real, double
		@Override
		public Double convert(String s) {
			return Double.parseDouble(s);
		}
		
		@Override
		public String unConvert(Object v) {
			checkType(Double.class, v);
			return (v == null) ? null : v.toString();
		}
	}
	
	public static class ByteArrayConverter implements IXConverter<byte[]> {
		// mysql type: binary, varbinary, tinyblob, blob, mediumblob, longblob. I have not finished the test.
		@Override
		public byte[] convert(String s) {
			return s.getBytes();
		}
		
		@Override
		public String unConvert(Object v) {
			checkType(byte[].class, v);
			return (v == null) ? null : new String((byte[])v);
		}
	}
	
	public static class BigIntegerConverter implements IXConverter<java.math.BigInteger> {
		// mysql type: unsigned bigint
		@Override
		public java.math.BigInteger convert(String s) {
			return new java.math.BigInteger(s);
		}
		
		@Override
		public String unConvert(Object v) {
			checkType(java.math.BigInteger.class, v);
			return (v == null) ? null : v.toString();
		}
	}
	
	public static class BigDecimalConverter implements IXConverter<java.math.BigDecimal> {
		// mysql type: decimal, numeric
		@Override
		public java.math.BigDecimal convert(String s) {
			return new java.math.BigDecimal(s);
		}
		
		@Override
		public String unConvert(Object v) {
			checkType(java.math.BigDecimal.class, v);
			return (v == null) ? null : v.toString();
		}
	}
	
	public static class BooleanConverter implements IXConverter<Boolean> {
		// mysql type: bit, tinyint(1)
		@Override
		public Boolean convert(String s) {
			String value = s.toLowerCase();
			if ("true".equals(value) || "1".equals(value) /* || "yes".equals(value) || "on".equals(value) */) {
				return Boolean.TRUE;
			}
			else if ("false".equals(value) || "0".equals(value) /* || "no".equals(value) || "off".equals(value) */) {
				return Boolean.FALSE;
			}
			else {
				throw new RuntimeException("Can not parse to boolean type of value: " + s);
			}
		}
		
		@Override
		public String unConvert(Object v) {
			checkType(Boolean.class, v);
			return (v == null) ? null : v.toString();
		}
	}
	
	public static class DateConverter implements IXConverter<java.util.Date> {
		// java.util.Date 类型专为传统 java bean 带有该类型的 setter 方法转换做准备，万不可去掉
		// 经测试 JDBC 不会返回 java.util.Data 类型。java.sql.Date, java.sql.Time,java.sql.Timestamp 全部直接继承自 java.util.Data, 所以 getDate可以返回这三类数据
		private String[] parsePattern = null;
		private String toStrPattern = null;
		public DateConverter() {}
		public DateConverter(String[] pattern) {
			this.parsePattern = pattern;
			this.toStrPattern = timeStampPattern;
		}
		public DateConverter(String[] pattern, String toStrPattern) {
			this.parsePattern = pattern;
			this.toStrPattern = toStrPattern;
		}
		public static DateConverter createDefaultDateConverter() {
			return new DateConverter(defaultParsePattern, timeStampPattern);
		}
		@Override
		public java.util.Date convert(String s) throws ParseException {
			if (this.parsePattern != null && this.parsePattern.length > 0) {
				return DateUtils.parseDate(s, this.parsePattern);
			}
			
			if (timeStampWithoutSecPatternLen == s.length()) {
				s = s + ":00";
			}
			if (s.length() > dateLen) {	// if (x < timeStampLen) 改用 datePattern 转换，更智能
				// Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]
				// return new java.util.Date(java.sql.Timestamp.valueOf(s).getTime());	// error under jdk 64bit(maybe)
				return DateUtils.parseDate(s, timeStampPattern);
			}
			else {
				// return new java.util.Date(java.sql.Date.valueOf(s).getTime());	// error under jdk 64bit
				return DateUtils.parseDate(s, datePattern);
			}
		}
		
		@Override
		public String unConvert(Object v) {
			checkType(java.util.Date.class, v);
			return DateFormatUtil.formatDate((toStrPattern!=null && toStrPattern.length()>0) ? toStrPattern : timeStampPattern, (java.util.Date)v);
		}
	}
	
	public static class SqlDateConverter implements IXConverter<java.sql.Date> {
		private String[] parsePattern = null;
		private String toStrPattern = null;
		public SqlDateConverter() {}
		public SqlDateConverter(String[] parsePattern) {
			this.parsePattern = parsePattern;
			this.toStrPattern = timeStampPattern;
		}
		public SqlDateConverter(String[] parsePattern, String toStrPattern) {
			this.parsePattern = parsePattern;
			this.toStrPattern = toStrPattern;
		}
		public static SqlDateConverter createDefaultSqlDateConverter() {
			return new SqlDateConverter(defaultParsePattern, timeStampPattern);
		}
		// mysql type: date, year
		@Override
		public java.sql.Date convert(String s) throws ParseException {
			if (this.parsePattern != null && this.parsePattern.length > 0) {
				return new java.sql.Date(DateUtils.parseDate(s, this.parsePattern).getTime());
			}
			
			if (timeStampWithoutSecPatternLen == s.length()) {
				s = s + ":00";
			}
			if (s.length() > dateLen) {	// if (x < timeStampLen) 改用 datePattern 转换，更智能
				// return new java.sql.Date(java.sql.Timestamp.valueOf(s).getTime());	// error under jdk 64bit(maybe)
				return new java.sql.Date(DateFormatUtil.parseDate(timeStampPattern, s).getTime());
			}
			else {
				// return new java.sql.Date(java.sql.Date.valueOf(s).getTime());	// error under jdk 64bit
				return new java.sql.Date(DateFormatUtil.parseDate(datePattern, s).getTime());
			}
		}
		
		@Override
		public String unConvert(Object v) {
			checkType(java.sql.Date.class, v);
			return DateFormatUtil.formatDate((toStrPattern!=null && toStrPattern.length()>0) ? toStrPattern : timeStampPattern, (java.sql.Date)v);
		}
	}
	
	public static class TimeConverter implements IXConverter<java.sql.Time> {
		private String[] parsePattern = null;
		private String toStrPattern = null;
		public TimeConverter() {}
		public TimeConverter(String[] parsePattern) {
			this.parsePattern = parsePattern;
			this.toStrPattern = "HH:mm:ss";
		}
		public TimeConverter(String[] parsePattern, String toStrPattern) {
			this.parsePattern = parsePattern;
			this.toStrPattern = toStrPattern;
		}
		public static TimeConverter createDefaultTimeConverter() {
			return new TimeConverter(new String[] {"HH:mm:ss", "HHmmss"}, "HH:mm:ss");
		}
		// mysql type: time
		@Override
		public java.sql.Time convert(String s) throws ParseException {
			if (this.parsePattern != null && this.parsePattern.length > 0) {
				return new java.sql.Time(DateUtils.parseDate(s, this.parsePattern).getTime());
			}
			
			int len = s.length();
			if (len == timeWithoutSecPatternLen) {
				s = s + ":00";
			}
			if (len > timePatternLen) {
				s = s.substring(0, timePatternLen);
			}
			return java.sql.Time.valueOf(s);
		}
		
		@Override
		public String unConvert(Object v) {
			checkType(java.sql.Time.class, v);
			return DateFormatUtil.formatDate((toStrPattern!=null && toStrPattern.length()>0) ? toStrPattern : "HH:mm:ss", (java.sql.Time)v);
		}
	}
	
	public static class TimestampConverter implements IXConverter<java.sql.Timestamp> {
		private String[] parsePattern = null;
		private String toStrPattern = null;
		public TimestampConverter() {}
		public TimestampConverter(String[] parsePattern) {
			this.parsePattern = parsePattern;
			this.toStrPattern = "HH:mm:ss";
		}
		public TimestampConverter(String[] parsePattern, String toStrPattern) {
			this.parsePattern = parsePattern;
			this.toStrPattern = toStrPattern;
		}
		public static TimestampConverter createDefaultTimestampConverter() {
			return new TimestampConverter(defaultParsePattern, timeStampPattern);
		}
		// mysql type: timestamp, datetime
		@Override
		public java.sql.Timestamp convert(String s) throws ParseException {
			if (this.parsePattern != null && this.parsePattern.length > 0) {
				return new java.sql.Timestamp(DateUtils.parseDate(s, this.parsePattern).getTime());
			}
			
			if (timeStampWithoutSecPatternLen == s.length()) {
				s = s + ":00";
			}
			if (s.length() > dateLen) {
				return java.sql.Timestamp.valueOf(s);
			}
			else {
				return new java.sql.Timestamp(DateFormatUtil.parseDate(datePattern, s).getTime());
			}
		}
		
		@Override
		public String unConvert(Object v) {
			checkType(java.sql.Timestamp.class, v);
			return DateFormatUtil.formatDate((toStrPattern!=null && toStrPattern.length()>0) ? toStrPattern : timeStampPattern, (java.sql.Timestamp)v);
		}
	}
	
	// -- 扩展类型转换 -- // 
	public static class IntegerArrayConverter implements IXConverter<Integer[]> {
		private String parseRegex = "[ ,]+"; // 默认支持英文空格、逗号来分隔
		private char toStrSpec = ',';	// 转字符串的分隔符

		public IntegerArrayConverter() {}
		public IntegerArrayConverter(String parseRegex, char toStrSpec) {
			this.parseRegex = parseRegex;
			this.toStrSpec = toStrSpec;
		}

		@Override
		public Integer[] convert(String s) throws ParseException {
			String[] arr = s.split(parseRegex);
			Integer[] intArr = new Integer[arr.length];
			for (int i=0; i<arr.length; ++i) {
				intArr[i] = Integer.valueOf(arr[i]);
			}
			return intArr;
		}

		@Override
		public String unConvert(Object v) {
			checkType(Integer[].class, v);
			return StringUtils.join((Integer[])v, toStrSpec);
		}
	}
	
	public static class PrimitiveIntegerArrayConverter implements IXConverter<int[]> {
		private String parseRegex = "[ ,]+"; // 默认支持英文空格、逗号来分隔
		private char toStrSpec = ',';	// 转字符串的分隔符

		public PrimitiveIntegerArrayConverter() {}
		public PrimitiveIntegerArrayConverter(String parseRegex, char toStrSpec) {
			this.parseRegex = parseRegex;
			this.toStrSpec = toStrSpec;
		}

		@Override
		public int[] convert(String s) throws ParseException {
			String[] arr = s.split(parseRegex);
			int[] intArr = new int[arr.length];
			for (int i=0; i<arr.length; ++i) {
				intArr[i] = Integer.parseInt(arr[i]);
			}
			return intArr;
		}

		@Override
		public String unConvert(Object v) {
			checkType(int[].class, v);
			return StringUtils.join((int[])v, toStrSpec);
		}
	}
	
	public static class PrimitiveLongArrayConverter implements IXConverter<long[]> {
		private String parseRegex = "[ ,]+"; // 默认支持英文空格、逗号来分隔
		private char toStrSpec = ',';	// 转字符串的分隔符

		public PrimitiveLongArrayConverter() {}
		public PrimitiveLongArrayConverter(String parseRegex, char toStrSpec) {
			this.parseRegex = parseRegex;
			this.toStrSpec = toStrSpec;
		}

		@Override
		public long[] convert(String s) throws ParseException {
			String[] arr = s.split(parseRegex);
			long[] retArr = new long[arr.length];
			for (int i=0; i<arr.length; ++i) {
				retArr[i] = Long.parseLong(arr[i]);
			}
			return retArr;
		}

		@Override
		public String unConvert(Object v) {
			checkType(long[].class, v);
			return StringUtils.join((long[])v, toStrSpec);
		}
	}
	
	public static class LongArrayConverter implements IXConverter<Long[]> {
		private String parseRegex = "[ ,]+"; // 默认支持英文空格、逗号来分隔
		private char toStrSpec = ',';	// 转字符串的分隔符

		public LongArrayConverter() {}
		public LongArrayConverter(String parseRegex, char toStrSpec) {
			this.parseRegex = parseRegex;
			this.toStrSpec = toStrSpec;
		}

		@Override
		public Long[] convert(String s) throws ParseException {
			String[] arr = s.split(parseRegex);
			Long[] retArr = new Long[arr.length];
			for (int i=0; i<arr.length; ++i) {
				retArr[i] = Long.valueOf(arr[i]);
			}
			return retArr;
		}

		@Override
		public String unConvert(Object v) {
			checkType(Long[].class, v);
			return StringUtils.join((Long[])v, toStrSpec);
		}
	}
	
	public static class PrimitiveFloatArrayConverter implements IXConverter<float[]> {
		private String parseRegex = "[ ,]+"; // 默认支持英文空格、逗号来分隔
		private char toStrSpec = ',';	// 转字符串的分隔符

		public PrimitiveFloatArrayConverter() {}
		public PrimitiveFloatArrayConverter(String parseRegex, char toStrSpec) {
			this.parseRegex = parseRegex;
			this.toStrSpec = toStrSpec;
		}

		@Override
		public float[] convert(String s) throws ParseException {
			String[] arr = s.split(parseRegex);
			float[] retArr = new float[arr.length];
			for (int i=0; i<arr.length; ++i) {
				retArr[i] = Float.parseFloat(arr[i]);
			}
			return retArr;
		}

		@Override
		public String unConvert(Object v) {
			checkType(float[].class, v);
			return StringUtils.join((float[])v, toStrSpec);
		}
	}
	
	public static class FloatArrayConverter implements IXConverter<Float[]> {
		private String parseRegex = "[ ,]+"; // 默认支持英文空格、逗号来分隔
		private char toStrSpec = ',';	// 转字符串的分隔符

		public FloatArrayConverter() {}
		public FloatArrayConverter(String parseRegex, char toStrSpec) {
			this.parseRegex = parseRegex;
			this.toStrSpec = toStrSpec;
		}

		@Override
		public Float[] convert(String s) throws ParseException {
			String[] arr = s.split(parseRegex);
			Float[] retArr = new Float[arr.length];
			for (int i=0; i<arr.length; ++i) {
				retArr[i] = Float.valueOf(arr[i]);
			}
			return retArr;
		}

		@Override
		public String unConvert(Object v) {
			checkType(Float[].class, v);
			return StringUtils.join((Float[])v, toStrSpec);
		}
	}
	
	public static class PrimitiveDoubleArrayConverter implements IXConverter<double[]> {
		private String parseRegex = "[ ,]+"; // 默认支持英文空格、逗号来分隔
		private char toStrSpec = ',';	// 转字符串的分隔符

		public PrimitiveDoubleArrayConverter() {}
		public PrimitiveDoubleArrayConverter(String parseRegex, char toStrSpec) {
			this.parseRegex = parseRegex;
			this.toStrSpec = toStrSpec;
		}

		@Override
		public double[] convert(String s) throws ParseException {
			String[] arr = s.split(parseRegex);
			double[] retArr = new double[arr.length];
			for (int i=0; i<arr.length; ++i) {
				retArr[i] = Double.parseDouble(arr[i]);
			}
			return retArr;
		}

		@Override
		public String unConvert(Object v) {
			checkType(double[].class, v);
			return StringUtils.join((double[])v, toStrSpec);
		}
	}
	
	public static class DoubleArrayConverter implements IXConverter<Double[]> {
		private String parseRegex = "[ ,]+"; // 默认支持英文空格、逗号来分隔
		private char toStrSpec = ',';	// 转字符串的分隔符

		public DoubleArrayConverter() {}
		public DoubleArrayConverter(String parseRegex, char toStrSpec) {
			this.parseRegex = parseRegex;
			this.toStrSpec = toStrSpec;
		}

		@Override
		public Double[] convert(String s) throws ParseException {
			String[] arr = s.split(parseRegex);
			Double[] retArr = new Double[arr.length];
			for (int i=0; i<arr.length; ++i) {
				retArr[i] = Double.valueOf(arr[i]);
			}
			return retArr;
		}

		@Override
		public String unConvert(Object v) {
			checkType(Double[].class, v);
			return StringUtils.join((Double[])v, toStrSpec);
		}
	}

}
